package com.dhy.socket.request.util;

import com.dhy.socket.anno.*;
import io.netty.handler.codec.http.HttpMethod;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Map;

/**
 * <p>
 *     对@Controller和@GetMapping,@PutMapping,@DeleteMapping,@PostMapping进行扫描处理
 * </p>
 * @author 大忽悠
 * @create 2022/6/20 20:44
 */

public class Scanner {


    public static void scanPackage(String packageName, File currentfile, Map<RequestKey, ControllerHolder> requestMappings) {
        File[] files = currentfile.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (currentfile.isDirectory()) {
                    return true;
                }
                return pathname.getName().endsWith(".class");
            }
        });

        for (File file : files) {
            if (file.isDirectory()) {
                scanPackage(packageName + "." + file.getName(), file,requestMappings);
            } else {
                // 若file非文件夹
                // 则要根据目前包路径和file名得到类名
                // 我们的包扫描是处理类的：
                // 所以我不希望他是八大基本类型，注解，接口，及，枚举
                String fileName = file.getName().replace(".class", "");
                String className = packageName + "." + fileName;
                try {
                    Class<?> klass = Class.forName(className);
                    if (klass.isAnnotation()
                            ||klass.isInterface()
                            ||klass.isEnum()
                            ||klass.isPrimitive()) {
                        continue;
                    }
                    // 无论普通包扫描和Jar包扫描
                    // 我们都需要对扫描到的类进行处理
                    // 如何处理并不由我们扫描的工具决定
                    // 是由工具使用者决定的
                    // 所以下面的dealClass必然是抽象方法！
                    dealClass(klass,requestMappings);
                } catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    private static void dealClass(Class<?> klass,Map<RequestKey, ControllerHolder> requestMappings) throws InstantiationException, IllegalAccessException {
       if(!klass.isAnnotationPresent(Controller.class)){
           return;
       }
        //不包含私有方法
        Method[] methods = klass.getMethods();
        for (Method method : methods) {
            handleMethodMapping(klass,method,requestMappings);
        }
    }

    private static void handleMethodMapping(Class<?> targetClass,Method method, Map<RequestKey, ControllerHolder> requestMappings) throws InstantiationException, IllegalAccessException {
             if(method.isAnnotationPresent(GetMapping.class)){
                 GetMapping getMapping = method.getAnnotation(GetMapping.class);
                 parseAnno(HttpMethod.GET,targetClass, method, requestMappings, getMapping.value());
             }else if(method.isAnnotationPresent(PostMapping.class)){
                 PostMapping postMapping = method.getAnnotation(PostMapping.class);
                 parseAnno(HttpMethod.POST,targetClass, method, requestMappings, postMapping.value());
             }else if(method.isAnnotationPresent(PutMapping.class)){
                 PutMapping putMapping = method.getAnnotation(PutMapping.class);
                 parseAnno(HttpMethod.PUT,targetClass, method, requestMappings, putMapping.value());
             }else if(method.isAnnotationPresent(DeleteMapping.class)){
                 DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class);
                 parseAnno(HttpMethod.DELETE,targetClass, method, requestMappings, deleteMapping.value());
             }
    }

    private static void parseAnno(HttpMethod httpMethod,Class<?> targetClass, Method method, Map<RequestKey, ControllerHolder> requestMappings,String uri) throws InstantiationException, IllegalAccessException {
        RequestKey requestKey = new RequestKey(httpMethod.toString(), uri);
        Object targetObj = targetClass.newInstance();
        ArrayList<ParamHolder> params = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : parameters) {
            if(!parameter.isAnnotationPresent(RequestParam.class)){
                continue;
            }
            RequestParam requestParam = parameter.getAnnotation(RequestParam.class);
            ParamHolder paramHolder = new ParamHolder(requestParam.value(), parameter.getType());
            params.add(paramHolder);
        }
        ControllerHolder controllerHolder = new ControllerHolder(method, targetObj, params);
        requestMappings.put(requestKey,controllerHolder);
    }
}
